package org.simpleframework.core;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.simpleframework.core.annotation.Component;
import org.simpleframework.core.annotation.Controller;
import org.simpleframework.core.annotation.Repository;
import org.simpleframework.core.annotation.Service;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author linchong
 * @version 1.0
 * @Date: 2022-05-22 15:38
 * @Description: CoreContainer$
 */
@Slf4j
//私有构造函数
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
	//1.8后，放弃之前分段锁，利用CAS和红黑树实现
	/**
	 * 存放所有被配置标记的目标对象的map
	 */
	private final Map<Class<?>,Object> beanMap = new  ConcurrentHashMap();

	/**
	 * 加载bean的注解列表
	 */
	private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
			= Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);

	/**
	 * 容器是否已经加载过bean
	 */
	private boolean loaded = false;


	/**
	 * 是否已经加载过bean
	 */
	public boolean isLoaded(){
		return loaded;
	}

	/**
	 * Bean实例的数量
	 * @return 数量
	 */
	public int size(){
		return beanMap.size();
	}


	/**
	 * 获取bean容器的实例
	 * @return
	 */
	public static BeanContainer getInstance(){
		return ContainerHolder.HOLDER.instance;
	}
	private enum ContainerHolder{
		HOLDER;
		private BeanContainer instance;
		ContainerHolder(){
			instance = new BeanContainer();
		}
	}

	/**
	 * 扫面加载所有的bean
	 * @param packageName 包名
	 */
	public synchronized void loadBeans(String packageName){
		//判断改容器是否被加载过，没有就加载
		if(isLoaded()){
			log.warn("BeanContainer has been loaded");
			return;
		}
		Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
		if(ValidationUtil.isEmpty(classSet)){
			log.warn("extract nothing from packageName "+packageName);
			return;
		}
		for (Class<?> clazz : classSet) {
			for(Class<? extends Annotation> annotation:BEAN_ANNOTATION){
				//如果类上标注了对应的注解，
				if(clazz.isAnnotationPresent(annotation)){
					//将目标类本身作为键，目标类的实例作为值，放入到beanMap中
					beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
				}
			}
		}
		loaded = true;
	}

	/**
	 * 添加一个class对象及其Bean实例
	 * @param clazz Class对象
	 * @param bean Bean实例
	 * @return 原有的Bean实例，没有则返回null
	 */
	public Object addBean(Class<?> clazz,Object bean){
		return beanMap.put(clazz,bean);
	}

	/**
	 * 移除一个IOC容器管理的对象
	 * @param clazz Class对象
	 * @return 返回删除的bean实例，没有则返回null
	 */
	public Object removeBean(Class<?> clazz){
		return beanMap.remove(clazz);
	}

	/**
	 * 根据Class对象获取Bean实例
	 * @param clazz Class镀锡
	 * @return Bean实例
	 */
	public Object getBean(Class<?> clazz){
		return beanMap.get(clazz);
	}

	/**
	 * 获取容器管理的所有Class对象集合
	 * @return Class集合
	 */
	public Set<Class<?>> getClasses(){
		return beanMap.keySet();
	}

	/**
	 * 获取所有的bean集合
	 * @return Bean集合
	 */
	public Set<Object> getBeans(){
		return new HashSet<>(beanMap.values());
	}

	/**
	 * 根据注解筛选出Bean的Class集合
	 * @param annotation 注解
	 * @return Class集合
	 */
	public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
		//1.获取beanMap中的所有class镀锡
		Set<Class<?>> keySet = getClasses();
		if(ValidationUtil.isEmpty(keySet)){
			log.warn("nothing in beanMap");
			return null;
		}
		//2.通过注解筛选出被注解标记的class对象，并添加到classSet中
		Set<Class<?>> classSet = new HashSet<>();
		for (Class<?> clazz : keySet) {
			//判断类是否有相关的注解标记
			if(clazz.isAnnotationPresent(annotation)){
				classSet.add(clazz);
			}
		}
		return classSet.size()> 0 ? classSet : null;
	}

	/**
	 * 通过接口或父类接口获取实现类或者子类的class集合，不包括其本身
	 *
	 * @param interfaceOrClass 接口Class或父类Class
	 * @return Class集合
	 */
	public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
		//1.获取beanMap中的所有class镀锡
		Set<Class<?>> keySet = getClasses();
		if(ValidationUtil.isEmpty(keySet)){
			log.warn("nothing in beanMap");
			return null;
		}
		//2.判断keySet中的元素是否是传入的接口或者类的子类，如果是，添加到classSet中
		Set<Class<?>> classSet = new HashSet<>();
		for (Class<?> clazz : keySet) {
			//判断keySet中的元素是否是传入的接口或者类的子类,排除本身
			if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
				classSet.add(clazz);
			}
		}
		return classSet.size()>0?classSet:null;
	}
}
